package com.xudow.app.util.rest;

import android.content.Context;

import com.activeshare.edu.ucenter.common.messages.Message;
import com.activeshare.edu.ucenter.models.base.AuthRole;
import com.xudow.app.Config;
import com.xudow.app.XApplication;
import com.xudow.app.model.SimpleUser;
import com.xudow.app.util.Maps;
import com.xudow.app.util.PrefUtils;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static com.xudow.app.util.LogUtils.LOGE;
import static com.xudow.app.util.LogUtils.makeLogTag;

/**
 * @author lukecheng
 */
public class XRestTemplate extends RestTemplate {
    private static final String TAG = makeLogTag(XRestTemplate.class);

    private HttpClient httpClient;
    private HttpContext httpContext;
    private StatefullHttpComponentsClientHttpRequestFactory statefullHttpComponentsClientHttpRequestFactory;

    private Context context;
    private CookieStore cs;

    public XRestTemplate(CookieStore cs, Context ctx) {
        super();

        context = ctx;
        this.cs = cs;

        HttpParams params = new BasicHttpParams();
        HttpClientParams.setRedirecting(params, false);

        httpClient = new DefaultHttpClient(params);
        httpContext = new BasicHttpContext();
        httpContext.setAttribute(ClientContext.COOKIE_STORE, cs);
        statefullHttpComponentsClientHttpRequestFactory =
                new StatefullHttpComponentsClientHttpRequestFactory(httpClient, httpContext);
        super.setRequestFactory(statefullHttpComponentsClientHttpRequestFactory);
    }

    private void recreateRestTemplate() {
        XApplication application = (XApplication) context.getApplicationContext();
        cs = application.getCookieStore();

        HttpParams params = new BasicHttpParams();
        HttpClientParams.setRedirecting(params, false);

        httpClient = new DefaultHttpClient(params);
        httpContext = new BasicHttpContext();
        httpContext.setAttribute(ClientContext.COOKIE_STORE, cs);
        statefullHttpComponentsClientHttpRequestFactory =
                new StatefullHttpComponentsClientHttpRequestFactory(httpClient, httpContext);
        super.setRequestFactory(statefullHttpComponentsClientHttpRequestFactory);
    }

    @Override
    public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException {
        T ret = null;

        try {
            ret = super.postForObject(url, request, responseType, uriVariables);

            if (null == ret) {
                boolean loginStatus = login();
                if (loginStatus) {
                    recreateRestTemplate();
                    ret = super.postForObject(url, request, responseType, uriVariables);
                }
            }
        } catch (RestClientException e) {
            boolean loginStatus = login();
            if (loginStatus) {
                recreateRestTemplate();
                ret = super.postForObject(url, request, responseType, uriVariables);
            } else {
                throw e;
            }
        }

        return ret;
    }

    @Override
    public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException {
        T ret = null;

        try {
            ret = super.getForObject(url, responseType, urlVariables);

            if (null == ret) {
                boolean loginStatus = login();
                if (loginStatus) {
                    recreateRestTemplate();
                    ret = super.getForObject(url, responseType, urlVariables);
                }
            }
        } catch (RestClientException e) {
            boolean loginStatus = login();
            if (loginStatus) {
                recreateRestTemplate();
                ret = super.getForObject(url, responseType, urlVariables);
            } else {
                throw e;
            }
        }

        return ret;
    }

    protected boolean login() {
        boolean ret = false;

        SimpleUser user = PrefUtils.getUserInfo(context);

        HashMap<String, String> params = Maps.newHashMap();
        params.put("username", user.getUsername());
        params.put("password", user.getPassword());

        RedirectStrategy redirectStrategy = new LaxRedirectStrategy();
        HttpClient httpClient = HttpClients.custom().setRedirectStrategy(redirectStrategy).build();

        CookieStore cookieStore = new BasicCookieStore();
        HttpContext clientContext = new BasicHttpContext();
        clientContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);

        HttpPost httpPost = new HttpPost(Config.BASE_URL + Config.LOGIN_URL);
        httpPost.setHeader("mobile", "true");

        String username = params.get("username");
        String password = params.get("password");

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("username", username));
        nvps.add(new BasicNameValuePair("password", password));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
        } catch (UnsupportedEncodingException e) {
            LOGE(TAG, e.getMessage());
        }

        try {
            HttpResponse response = httpClient.execute(httpPost, clientContext);
            int status = response.getStatusLine().getStatusCode();

            if (HttpStatus.OK.value() == status) {
                String result = EntityUtils.toString(response.getEntity());

                JSONTokener jsonParser = new JSONTokener(result);
                try {
                    JSONObject jsonObject = (JSONObject) jsonParser.nextValue();

                    Message msg = new Message();
                    msg.setErrorCode(jsonObject.getString("errorCode"));
                    msg.setResult(jsonObject.getString("result"));
                    msg.setErrorMessage(jsonObject.getString("errorMessage"));

                    if ("0".equals(msg.getResult())) {
                        //登陆成功
                        XApplication application = (XApplication) context.getApplicationContext();
                        application.setCookieStore(cookieStore);

                        //保存用户信息到Preference
                        SimpleUser simpleUser = new SimpleUser();
                        simpleUser.setUsername(username);
                        simpleUser.setPassword(password);

                        PrefUtils.saveUserInfo(context, simpleUser);
                        PrefUtils.setPrefLoginStatus(context, true);

                        //角色
                        List<AuthRole> roles = new ArrayList<AuthRole>();

                        JSONArray roleArr = jsonObject.getJSONArray("roles");
                        for (int i = 0; i < roleArr.length(); i++) {
                            JSONObject roleJSON = roleArr.getJSONObject(i);

                            AuthRole role = new AuthRole();
                            role.setAbbreviation(roleJSON.getString("abbreviation"));
                            role.setDescription(roleJSON.getString("description"));
                            role.setId(roleJSON.getLong("id"));
                            role.setName(roleJSON.getString("name"));

                            roles.add(role);
                        }

                        application.setRoles(roles);

                        ret = true;
                    } else {
                        //登录失败
                        ret = false;
                    }
                } catch (JSONException e) {
                    LOGE(TAG, e.getMessage());
                }
            } else {
                //登录失败
                ret = false;
            }
        } catch (IOException e) {
            LOGE(TAG, e.getMessage());

            ret = false;
        }

        return ret;
    }

    public void reInitMessageConverter() {
        List<HttpMessageConverter<?>> converterList = this.getMessageConverters();
        HttpMessageConverter<?> converterTarget = null;
        for (HttpMessageConverter<?> item : converterList) {
            if (item.getClass() == StringHttpMessageConverter.class) {
                converterTarget = item;
                break;
            }
        }

        if (converterTarget != null) {
            converterList.remove(converterTarget);
        }
        HttpMessageConverter<?> converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        converterList.add(converter);
    }

    public class StatefullHttpComponentsClientHttpRequestFactory extends
            HttpComponentsClientHttpRequestFactory {
        private final HttpContext httpContext;

        public StatefullHttpComponentsClientHttpRequestFactory(
                HttpClient httpClient, HttpContext httpContext) {
            super(httpClient);
            this.httpContext = httpContext;
        }

        @Override
        protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
            return this.httpContext;
        }
    }

}
